# Process with CMake to generate project of choice
CMAKE_MINIMUM_REQUIRED (VERSION 2.8.6)  # Require at least CMake 2.8.6

PROJECT (libcyan C CXX)

SET (LC_MAJOR_VERSION    0)
SET (LC_MINOR_VERSION    0)
SET (LC_UPDATE_VERSION   0)
SET (LC_REVISION         68)
SET (LC_APIVERSION       0)

INCLUDE (TestBigEndian)
INCLUDE (InstallRequiredSystemLibraries)
INCLUDE (CheckTypeSize)
INCLUDE (TestForANSIStreamHeaders)
INCLUDE (TestForSTDNamespace)
INCLUDE (TestForANSIForScope)
INCLUDE (CMakeBackwardCompatibilityCXX)
INCLUDE (CheckIncludeFileCXX)
INCLUDE (CheckCXXSymbolExists)
INCLUDE (CheckCXXCompilerFlag)
INCLUDE (CheckCXXSourceCompiles)

# CMake default configuration
IF (NOT CMAKE_VERBOSE_MAKEFILE)
  SET (CMAKE_VERBOSE_MAKEFILE ON CACHE STRING
      "Makefile verbosity"
      FORCE)
ENDIF (NOT CMAKE_VERBOSE_MAKEFILE)

IF (NOT CMAKE_BUILD_TYPE)
  SET (CMAKE_BUILD_TYPE None CACHE STRING
      "Choose the type of build, options are: None Debug Release RelWithDebInfo MinSizeRel."
      FORCE)
  SET_PROPERTY (GLOBAL PROPERTY DEBUG_CONFIGURATIONS Debug RelWithDebugInfo)
ENDIF (NOT CMAKE_BUILD_TYPE)

IF (NOT CMAKE_SKIP_RPATH)
  SET (CMAKE_SKIP_RPATH ON CACHE STRING
      "If set, runtime paths are not added when using shared libraries."
      FORCE)
ENDIF (NOT CMAKE_SKIP_RPATH)

IF (NOT CMAKE_USE_RELATIVE_PATHS)
  SET (CMAKE_USE_RELATIVE_PATHS ON CACHE STRING
      "If true, cmake will use relative paths in makefiles and projects."
      FORCE)
ENDIF (NOT CMAKE_USE_RELATIVE_PATHS)

IF (NOT BUILD_SHARED_LIBS)
  SET (BUILD_SHARED_LIBS ON CACHE STRING
      "If set, builds shared library for libcyan"
      FORCE)
ENDIF (NOT BUILD_SHARED_LIBS)

# System specific probes and definitions
IF (CMAKE_SYSTEM_LOADED)
  STRING (TOUPPER ${CMAKE_HOST_SYSTEM_NAME} LC_SYSTEM_NAME)
  ADD_DEFINITIONS (-DLC_OS="${LC_SYSTEM_NAME}" -DLC_OS_${LC_SYSTEM_NAME})
  IF (UNIX)
    ADD_DEFINITIONS (-DLC_OS_UNIX_LIKE)
  ENDIF (UNIX)
  STRING (TOUPPER ${CMAKE_HOST_SYSTEM_PROCESSOR} LC_SYSTEM_ARCH)
  ADD_DEFINITIONS (-DLC_ARCH="${LC_SYSTEM_ARCH}" -DLC_ARCH_${LC_SYSTEM_ARCH})
  TEST_BIG_ENDIAN (WORDS_BIGENDIAN)
ENDIF (CMAKE_SYSTEM_LOADED)

# Compiler specific probes and definitions
IF (CMAKE_CXX_COMPILER_LOADED)
ENDIF (CMAKE_CXX_COMPILER_LOADED)

# Project meta-data definitions
MATH (EXPR LC_AGE "${LC_APIVERSION} - 1")
IF (LC_AGE LESS 0)
  SET (LC_AGE 0)
ENDIF (LC_AGE LESS 0)
SET (PACKAGE            "${CMAKE_PROJECT_NAME}")
SET (PACKAGE_NAME       "${CMAKE_PROJECT_NAME}")
SET (PACKAGE_TARNAME    "${CMAKE_PROJECT_NAME}")
SET (PACKAGE_VERSION    "${LC_MAJOR_VERSION}.${LC_MINOR_VERSION}.${LC_UPDATE_VERSION}")
SET (PACKAGE_STRING     "${PACKAGE_NAME} ${PACKAGE_VERSION}")
SET (PACKAGE_URL        "http://libcyan.googlecode.com/")
SET (PACKAGE_BUGREPORT  "obelix@codegrafix.com")
SET (PACKAGE_SOVERSION  "${LC_APIVERSION}:${LC_REVISION}:${LC_AGE}")

  
# Include header probes
SET (LC_CHECK_HEADERS
  cstdlib cstdio cstring cwchar cmath exception stdexcept new cerrno cassert
  cstdarg wctype cstdint unistd.h fcntl.h sched.h time.h sys/time.h sys/types.h
  sys/resource.h sys/sysctl.h ioctl.h sys/ioctl.h sys/stat.h Windows.h
  bits/atomicity.h ext/atomicity.h stdint.h inttypes.h pthread.h
)

UNSET (LC_AVAILABLE_HEADERS)
FOREACH (LC_HEADER ${LC_CHECK_HEADERS})
  STRING (REGEX REPLACE "[./]" "_" LC_MACRO_NAME "HAVE_${LC_HEADER}")
  STRING (TOUPPER ${LC_MACRO_NAME} LC_MACRO_NAME)
  CHECK_INCLUDE_FILE_CXX (${LC_HEADER} ${LC_MACRO_NAME})
  IF (${${LC_MACRO_NAME}})
    LIST (APPEND LC_AVAILABLE_HEADERS ${LC_HEADER})
  ENDIF (${${LC_MACRO_NAME}})
ENDFOREACH (LC_HEADER ${LC_REQUIRED_HEADERS})
LIST (REMOVE_DUPLICATES LC_AVAILABLE_HEADERS)

# Type sizes
SET (LC_CHECK_TYPES
  int8_t    int16_t   int32_t   int64_t
  uint8_t   uint16_t  uint32_t  uint64_t
  size_t    ssize_t   off_t     ptrdiff_t
  clock_t   time_t    char      wchar_t
  wint_t
)

FOREACH (LC_TYPE ${LC_CHECK_TYPES})
  STRING (TOUPPER "LC_SIZEOF_${LC_TYPE}" LC_SIZEOF_MACRO)
  STRING (TOUPPER "HAVE_${LC_TYPE}" LC_HAVE_MACRO)
  CHECK_TYPE_SIZE (${LC_TYPE} ${LC_SIZEOF_MACRO})
  IF (${${LC_SIZEOF_MACRO}})
    SET (${LC_HAVE_MACRO} TRUE)
  ENDIF (${${LC_SIZEOF_MACRO}})
ENDFOREACH (LC_TYPE ${LC_CHECK_TYPES})

CHECK_CXX_SYMBOL_EXISTS (stat::st_dev     "${LC_AVAILABLE_HEADERS}" HAVE_STRUCT_STAT_ST_DEV)
CHECK_CXX_SYMBOL_EXISTS (stat::st_atime   "${LC_AVAILABLE_HEADERS}" HAVE_STRUCT_STAT_ST_ATIME)
CHECK_CXX_SYMBOL_EXISTS (stat::st_mtime   "${LC_AVAILABLE_HEADERS}" HAVE_STRUCT_STAT_ST_MTIME)
CHECK_CXX_SYMBOL_EXISTS (stat::st_ctime   "${LC_AVAILABLE_HEADERS}" HAVE_STRUCT_STAT_ST_CTIME)
CHECK_CXX_SYMBOL_EXISTS (stat::st_size    "${LC_AVAILABLE_HEADERS}" HAVE_STRUCT_STAT_ST_SIZE)
CHECK_CXX_SYMBOL_EXISTS (stat::st_blksize "${LC_AVAILABLE_HEADERS}" HAVE_STRUCT_STAT_ST_BLKSIZE)
CHECK_CXX_SYMBOL_EXISTS (stat::st_mode    "${LC_AVAILABLE_HEADERS}" HAVE_STRUCT_STAT_ST_MODE)
CHECK_CXX_SYMBOL_EXISTS (stat::st_blocks  "${LC_AVAILABLE_HEADERS}" HAVE_STRUCT_STAT_ST_BLOCKS)
CHECK_CXX_SYMBOL_EXISTS (_stat::st_dev     "${LC_AVAILABLE_HEADERS}" HAVE_STRUCT__STAT_ST_DEV)
CHECK_CXX_SYMBOL_EXISTS (_stat::st_atime   "${LC_AVAILABLE_HEADERS}" HAVE_STRUCT__STAT_ST_ATIME)
CHECK_CXX_SYMBOL_EXISTS (_stat::st_mtime   "${LC_AVAILABLE_HEADERS}" HAVE_STRUCT__STAT_ST_MTIME)
CHECK_CXX_SYMBOL_EXISTS (_stat::st_ctime   "${LC_AVAILABLE_HEADERS}" HAVE_STRUCT__STAT_ST_CTIME)
CHECK_CXX_SYMBOL_EXISTS (_stat::st_size    "${LC_AVAILABLE_HEADERS}" HAVE_STRUCT__STAT_ST_SIZE)
CHECK_CXX_SYMBOL_EXISTS (_stat::st_blksize "${LC_AVAILABLE_HEADERS}" HAVE_STRUCT__STAT_ST_BLKSIZE)
CHECK_CXX_SYMBOL_EXISTS (_stat::st_mode    "${LC_AVAILABLE_HEADERS}" HAVE_STRUCT__STAT_ST_MODE)
CHECK_CXX_SYMBOL_EXISTS (_stat::st_blocks  "${LC_AVAILABLE_HEADERS}" HAVE_STRUCT__STAT_ST_BLOCKS)
CHECK_CXX_SYMBOL_EXISTS (stat::st_atimespec   "${LC_AVAILABLE_HEADERS}" HAVE_STRUCT_STAT_ST_ATIMESPEC)
CHECK_CXX_SYMBOL_EXISTS (stat::st_mtimespec   "${LC_AVAILABLE_HEADERS}" HAVE_STRUCT_STAT_ST_MTIMESPEC)
CHECK_CXX_SYMBOL_EXISTS (stat::st_ctimespec   "${LC_AVAILABLE_HEADERS}" HAVE_STRUCT_STAT_ST_CTIMESPEC)
CHECK_CXX_SYMBOL_EXISTS (__gnu_cxx::__exchange_and_add "${LC_AVAILABLE_HEADERS}" HAVE___EXCHANGE_AND_ADD)

CHECK_CXX_SOURCE_COMPILES ("void func() __attribute__((noreturn));int main(){return 0;}" HAVE___ATTRIBUTE__)
CHECK_CXX_SOURCE_COMPILES ("void func() __declspec((noreturn));int main(){return 0;}" HAVE___DECLSPEC)


SET     (CMAKE_THREAD_PREFER_PTHREAD ON)
INCLUDE (FindThreads)

IF (Threads_FOUND)
  LINK_LIBRARIES (${CMAKE_THREAD_LIBS_INIT})
ELSE (Threads_FOUND)
  MESSAGE (FATAL_ERROR "No suitable multi-threading API found!")
ENDIF (Threads_FOUND)

SET (libcyan_headers
  include/lc_atomic_word.h
  include/lc_avl_tree.h
  include/lc_basic_string.h
  include/lc_char_traits.h
  include/lc_condition_variable.h
  include/lc_delegate.h
  include/lc_exception.h
  include/lc_file.h
  include/lc_forward_decl.h
  include/lc_interval.h
  include/lc_iterator.h
  include/lc_linked_list.h
  include/lc_linked_list_impl.h
  include/lc_multi_reader_lock.h
  include/lc_mutex.h
  include/lc_normal_iterator.h
  include/lc_numeric_limits.h
  include/lc_object.h
  include/lc_pair.h
  include/lc_scheduler.h
  include/lc_scope_guard.h
  include/lc_scoped_pointer.h
  include/lc_shutdown_policy.h
  include/lc_system.h
  include/lc_system_exception.h
  include/lc_thread.h
  include/lc_thread_manager.h
  include/lc_thread_storage.h
  include/lc_thread_storage_pthread.h
  include/lc_types.h
  include/libcyan.h
  include/libcyan_internal.h
)

SET (libcyan_sources
  src/lc_atomic_word.cpp
  src/lc_exception.cpp
  src/lc_system_exception.cpp
  src/lc_basic_string.cpp
  src/lc_file.cpp
  src/lc_thread_manager.cpp
  src/lc_interval.cpp
  src/lc_system.cpp
)

SET (libcyan_pthread_sources
  src/lc_thread_pthread.cpp
  src/lc_condition_variable_pthread.cpp
  src/lc_mutex_pthread.cpp
  src/lc_multi_reader_lock_pthread.cpp
)

SET (libcyan_unit_test_headers
  test/include/lc_test_char_traits.h
  test/include/lc_test_thread.h
  test/include/lc_test_avl_tree.h
)

SET (libcyan_unit_test_sources
  test/src/lc_test_main.cpp
  test/src/lc_test_char_traits.cpp
  test/src/lc_test_thread.cpp
  test/src/lc_test_avl_tree.cpp
)

SET (libgtest_headers
  test/include/gtest/gtest.h
  test/include/gtest/gtap.h
)

SET (libgtest_sources
  test/src/gtest-all.cc
)

INCLUDE_DIRECTORIES (${PROJECT_BINARY_DIR})
INCLUDE_DIRECTORIES (${PROJECT_SOURCE_DIR}/include)

IF (CMAKE_USE_PTHREADS_INIT)
  SET (HAVE_PTHREAD TRUE)
  LIST (APPEND libcyan_sources ${libcyan_pthread_sources})
ELSEIF (CMAKE_USE_WIN32_THREADS_INIT)
  SET (HAVE_WIN32_THREAD TRUE)
  # Include WIN32_THREAD sources
ELSE (CMAKE_USE_PTHREADS_INIT)
  MESSAGE (FATAL_ERROR "No suitable multi-threading API found!")
ENDIF (CMAKE_USE_PTHREADS_INIT)

ADD_LIBRARY (cyan ${libcyan_sources})
SET_TARGET_PROPERTIES (cyan PROPERTIES VERSION "${PACKAGE_VERSION}")

#ADD_LIBRARY (gtest ${libgtest_sources})

CONFIGURE_FILE (${PROJECT_SOURCE_DIR}/lc_config.h.cmake
  ${PROJECT_BINARY_DIR}/lc_config.h)
ADD_DEFINITIONS (-DHAVE_CONFIG_H -D__CYAN_BUILD_DLL__)
